<?php


namespace app\api\service;


use app\api\model\OrderProduct;
use app\api\model\Goods;

use app\api\model\Order as OrderModel;
use app\api\model\UserAddress;
use app\lib\enum\OrderStatusEnum;
use app\lib\exception\OrderException;
use app\lib\exception\UserException;
use think\Db;
use think\Exception;

use redis\Redis;

/**
 * 订单类
 * 订单做了以下简化：
 * 创建订单时会检测库存量，但并不会预扣除库存量，因为这需要队列支持
 * 未支付的订单再次支付时可能会出现库存不足的情况
 * 所以，项目采用3次检测
 * 1. 创建订单时检测库存
 * 2. 支付前检测库存
 * 3. 支付成功后检测库存
 */
class Order
{
    
    protected $products;
    protected $uid;

    private $basket      = [];     //私有空数组用于存放商品信息
    // private $cachekey    = Null;
    private $expire      = 43200;  //redis购物车商品缓存过期时间

    /**
     * @param int $uid 用户id
     * @param array $oProducts 订单商品列表
     * @return array 订单商品状态
     * @throws Exception
     * 创建订单的第一步
     */
    public function place($uid, $Products,$coupons_id='')
    {

        $this->coupons_id = $coupons_id;
        $this->products = $Products;
        
        $this->uid = $uid;
        //没有多做一步验证（根据id去数据查询进行验证）
        $status = $this->getOrderStatus();
        if (!$status['pass']) {
            $status['order_id'] = -1;
            return $status;
        }
        //开始创建订单
        $orderSnap = $this->snapOrder();//生成订单快照信息
        $status = self::createOrderByTrans($orderSnap);
        $status['pass'] = true;
        if($orderSnap && $status){
        $this->redis_ext    = '.'.'redis';      //redis缓存键名拼接项目中文名称字符串
        $this->cachekey     = 'user.cart'.$uid.$this->redis_ext;  //redis缓存键名拼接用户ID与项目中文名称字符串为对象用户购物车缓存键名
        $this->basket       = json_decode(redis()->get($this->cachekey),true);  
        foreach ($Products as $key => $value) {
            $this->dele($value['goods_id'],$value['attr_id']);
        }
        }
        return_json(200,'订单生成成功',$status);
    }
//验证商品的库存量，累加价格数量
     private function getOrderStatus()
    {
        $status = [
            'pass' => true,//订单是否通过
            'totalCount'=>0,
            'orderPrice' => 0,//所有商品价格的总和
            'pStatusArray' => []//保存所有商品的详细信息

        ];
        foreach ($this->products as $oProduct) {
            $pStatus =
                $this->getProductStatus(
                    $oProduct['goods_id'], $oProduct['goods_number'], $this->products);
              
            if (!$pStatus['haveStock']) {
                $status['pass'] = false;
            }
            //$status['orderPrice']订单价格通过每一类商品的总价格相加得到
            $status['orderPrice'] += $pStatus['totalPrice'];
            $status['totalCount'] += $pStatus['goods_number'];
            array_push($status['pStatusArray'], $pStatus);
        }
        return $status;
    }

//验证商品是否存在和库存量
        private function getProductStatus($oPID, $oCount, $products)
    {   //当前传过来的$oPID在$products的序号，默认-1
        $pIndex = -1;
        //$pStatus保存某一个商品的详细信息
        $pStatus = [
            'goods_id' => null,//商品id
            'haveStock' => false,//是否有库存量，默认否
            'goods_number' => 0,//商品订单请求的数量
            'goods_price' => 0,
            'goods_title' => '',//商品名
            'totalPrice' => 0,//商品价格
            'title_thumb'=> null,
            'goods_attr_str'=>'',
        ];
// dump($products);
        for ($i = 0; $i < count($products); $i++) {
            if ($oPID == $products[$i]['goods_id']) {//商品表是id所以查id
                $pIndex = $i;
            }
        }

        if ($pIndex == -1) {
            // 客户端传递的productid有可能根本不存在
            throw new OrderException(
                [
                    'msg' => 'id为' . $oPID . '的商品不存在，订单创建失败'
                ]);
        } else {
            $product = $products[$pIndex];
            $pStatus['goods_id'] = $product['goods_id'];
            $pStatus['goods_title'] = $product['goods_title'];
            $pStatus['goods_number'] = $oCount;
            $pStatus['goods_price'] = $product['goods_price'];
            $pStatus['goods_attr_str'] = $product['goods_attr_str'];
            $pStatus['title_thumb'] = $product['title_thumb'];
            $pStatus['totalPrice'] = $product['goods_price'] * $oCount;
         foreach ($product['attr_id'] as $k => $v) {
             $stock[] = $this->stock($product['goods_id'],$v);
         }
         
         $stock = min($stock);

         
            if ($stock - $oCount >= 0) {
                $pStatus['haveStock'] = true;
            }
        }
        return $pStatus;
    }
    
    //库存量
    private function stock($goods_id,$attr_id){
        $goods = db('goods_attr')->field('attr_stock')->where('goods_id',$goods_id)->where('id',$attr_id)->find();

        $stock = $goods['attr_stock'];
       return $stock;
    }


      // 预检测并生成订单快照
    private function snapOrder()
    {

        // status可以单独定义一个类
        $snap = [
            'orderPrice' => 0,
            'totalCount' => 0,
            'goods_total_price'=>0,
            'pStatus' => [],
            'snapAddress' => json_encode($this->getUserAddress()),//收货地址
          
        
        ];

     

            for ($i = 0; $i < count($this->products); $i++) {
                $product = $this->products[$i];
                // dump($product);
                //在这减库存量
            
                foreach ($product['attr_id'] as $k => $v) {
                   db('goods_attr')->where('id',$v)->where('goods_id',$product['goods_id'])->setDec('attr_stock',$product['goods_number']);
                }
                
         
               

                $pStatus = $this->snapProduct($product, $product['goods_number']);
               
                $snap['orderPrice'] += $pStatus['totalPrice'];
                $snap['goods_total_price'] += $pStatus['goods_total_price'];
                $snap['totalCount'] += $pStatus['goods_number'];
                array_push($snap['pStatus'], $pStatus);
            }
       
        return $snap;
    }

     //拿地址
     private function getUserAddress()
    {
        $userAddress = UserAddress::where('uid', '=', $this->uid)
            ->find();
        if (!$userAddress) {
            throw new UserException(
                [
                    'msg' => '用户收货地址不存在，下单失败',
                    'errorCode' => 60001,
                ]);
        }
        return $userAddress->toArray();
    }


      // 价格的结算
    private function snapProduct($product, $oCount)
    {

        $pStatus = [
            'goods_id' => null,
            'goods_title' => null,
            'title_thumb'=>null,
            'goods_number' => $oCount,
            'totalPrice' => 0,
            'goods_total_price' => 0
        ];

        $pStatus['goods_number'] = $oCount;
        // 以服务器价格为准，生成订单
        $user = db('user')->field('vip')->where('id',$this->uid)->find();
        $coupons_id = $this->coupons_id;
        if(!empty($coupons_id)){
            $uid = $this->uid;
            $coupons = db('coupons_taken')->where('uid',$uid)->where('coupons_id',$coupons_id)->where('use_status',0)->update(['use_status'=>1]);
       
       if($coupons){
            $price = db('coupons')->where('id',$coupons_id)->field('goods_total_price,minus_price')->find();
     
       if( $oCount * $product['goods_price'] > $price['goods_total_price'] ||  $oCount * $product['goods_price'] == $price['goods_total_price'] ){

         $pStatus['totalPrice'] = $oCount * $product['goods_price'] - $price['minus_price'];
        }else{
            $pStatus['totalPrice'] = $oCount * $product['goods_price'];
        }
        }
        }else{
             $pStatus['totalPrice'] = $oCount * $product['goods_price'];
        }
      
       
       
       
        if($user['vip'] == 1){

        $pStatus['totalPrice'] = $pStatus['totalPrice'] * 0.85;
        }else{
            $pStatus['totalPrice'] = $pStatus['totalPrice'];
        }
        $pStatus['goods_total_price'] = $oCount * $product['goods_price'];

        $pStatus['goods_price'] = $product['goods_price'];
        $pStatus['goods_title'] = $product['goods_title'];
        $pStatus['goods_id'] = $product['goods_id'];
        $pStatus['title_thumb'] =$product['title_thumb'];
        $pStatus['goods_attr_str'] = $product['goods_attr_str'];


        return $pStatus;
    }


    // 创建订单时没有预扣除库存量，简化处理
    // 如果预扣除了库存量需要队列支持，且需要使用锁机制
    private function createOrderByTrans($snap)
    {
        //Db::startTrans();Db::commit();是一个开始和结束的过程，避免中途停止数据不完整
        Db::startTrans();//开始
        try {
         
            $orderNo = $this->makeOrderNo();//生成订单号
            $order = new OrderModel();
            $order->user_id = $this->uid;
            $order->out_trade_no = $orderNo;//填充给模型字段订单号

            $order->order_total_price = $snap['orderPrice'];
            $order->goods_total_price = $snap['goods_total_price'];

            
            $order->total_count = $snap['totalCount'];
            // $order->snap_img = $snap['snapImg'];
            // $order->snap_name = $snap['snapName'];
            $order->address = $snap['snapAddress'];
            $order->snap_items = json_encode($snap['pStatus']);//数组元素序列化
            $order->save();//写入数据库

            $orderID = $order->id;
            $create_time = $order->create_time;//订单创建时间

            //修改oProducts，为每一个order_id赋值
            foreach ($this->products as &$p) {
                $p['order_id'] = $orderID;
            }
          
            model('orderGoods')->saveAll($this->products);
            Db::commit();//结束
            return [
                'order_no' => $orderNo,
                'order_id' => $orderID,
                'create_time' => $create_time
            ];
        } catch (Exception $ex) {
            Db::rollback();//数据回滚,跟Db::startTrans();Db::commit();一起的
            throw $ex;
        }
    }


        //最后一步
    public static function makeOrderNo()
    {
        $yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
        $orderSn =
            $yCode[intval(date('Y')) - 2020] . strtoupper(dechex(date('m'))) . date(
                'd') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf(
                '%02d', rand(0, 99));
        return $orderSn;
    }


     // 删除redis缓存的购物车
   public function dele($ids){
        if(is_array($ids)){
            foreach ($ids as $v){
                foreach ($this->basket as $k=>$val) {
                    if( $val['id'] == $v['id'] && $val['attrid'] == $v['attrid'] ){
                        array_splice($this->basket,$k,1);
                    }
                }
            }
        }else{
            foreach ($this->basket as $k=>$val) {
                if( $val['id'] == $ids){
                   
                    array_splice($this->basket,$k,1);
                }
            }
        }
        redis()->setex($this->cachekey,$this->expire,json_encode($this->basket));
        return true;
    }

}